Demo: Geo Mapping with Plotly - Choropleth

The process of producing visual representations of geographic data, frequently on a map or in another geographical context, is referred to as geo-mapping. Depending on their geographic coordinates, points, lines, or areas can be displayed on a map.

Choropleth Map

Choropleth Maps display divided geographical areas or regions that are coloured, shaded or patterned in relation to a data variable. This provides a way to visualise values over a geographical area, which can show variation or patterns across the displayed location.

The data variable uses colour progression to represent itself in each region of the map. Typically, this can be a blending from one colour to another, a single hue progression, transparent to opaque, light to dark or an entire colour spectrum.

One downside to the use of colour is that you can't accurately read or compare values from the map.
Another issue is that larger regions appear more emphasised then smaller ones, so the viewer's perception of the shaded values are affected.
A common error when producing Choropleth Maps is to encode raw data values (such as population) rather than using normalized values (calculating population per square kilometre for example) to produce a density map.

image.png

Plotly

Plotly is an open source library for Python. It integrates greatly with Jupyter Notebook and Dash to create interactive content for websites. It can be used to create ad hoc charts and professional content.

Handling spatial data for Geo Mapping needs some additional datasets and basic setup before you can use it for visual data representation and analysis.

E.g.Choropleth Charts are colored shapes and those shapes are of course manyfold. If you read through Plotly Documentation you can see that there are default shapes available in plotly for USA States and Countries as defined in the Natural Earth datasets.

To visualize data for Switzerland geo regions like cantons, districts or municipalities, we must explore other data sources for corresponding shapes to handle with.


Source/Links:

History:


Introduction

What to consider when creating choropleth maps

Maps are not objective, but a version of reality. When creating them, lots of choices are made: What to map, how to map and whether or not to use a map in the first place?

How to make your choropleth maps better

Consider a sequential color scheme if you want to drive attention to the high values, e.g. for unemployment rates. Consider a diverging scheme if you want to drive the attention to both extremes of the scale, e.g. too show the difference in votes between two competing parties. With any color scheme, do use colorblind-friendly colors.

image.png


image.png

What is GeoPandas?

The goal of GeoPandas is to make working with geospatial data in python easier. It combines the capabilities of pandas and shapely, providing geospatial operations in pandas and a high-level interface to multiple geometries to shapely. GeoPandas enables you to easily do operations in python that would otherwise require a spatial database such as PostGIS.

GeoPandas, as the name suggests, extends the popular data science library pandas by adding support for geospatial data.

The core data structure in GeoPandas is the geopandas.GeoDataFrame, a subclass of pandas.DataFrame, that can store geometry columns and perform spatial operations.

The geopandas.GeoSeries, a subclass of pandas.Series, handles the geometries.

Therefore, your GeoDataFrame is a combination of pandas.Series, with traditional data (numerical, boolean, text etc.), and geopandas.GeoSeries, with geometries (points, polygons etc.).

You can have as many columns with geometries as you wish; there’s no limit typical for desktop GIS software.

image.png


Example 1: A Swiss Choropleth Plot Map using Swiss Cantons

Setup Libraries and Data

!pip install pandas_geojson
Collecting pandas_geojson
  Downloading pandas_geojson-2.0.0-py3-none-any.whl (12 kB)
Requirement already satisfied: pandas in /usr/local/lib/python3.10/dist-packages (from pandas_geojson) (1.5.3)
Requirement already satisfied: python-dateutil>=2.8.1 in /usr/local/lib/python3.10/dist-packages (from pandas->pandas_geojson) (2.8.2)
Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.10/dist-packages (from pandas->pandas_geojson) (2023.4)
Requirement already satisfied: numpy>=1.21.0 in /usr/local/lib/python3.10/dist-packages (from pandas->pandas_geojson) (1.25.2)
Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.10/dist-packages (from python-dateutil>=2.8.1->pandas->pandas_geojson) (1.16.0)
Installing collected packages: pandas_geojson
Successfully installed pandas_geojson-2.0.0
# Load GEOPANDAS library
import geopandas as gpd
# read Swiss Cantons Data + Geometry
geojson_file = 'https://raw.githubusercontent.com/sawubona-repo/BINA-FS24-WORK/master/zDiversExamples/Notebook-GeoMapping/DATA/ch-cantons.geojson'

# Read GeoJSON file into GeoDataFrame
geodf = gpd.read_file(geojson_file)
geodf.head()
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
UUID DATUM_AEND DATUM_ERST ERSTELL_J ERSTELL_M REVISION_J REVISION_M GRUND_AEND HERKUNFT HERKUNFT_J ... OBJEKTART REVISION_Q ICC KANTONSNUM SEE_FLAECH KANTONSFLA KT_TEIL NAME EINWOHNERZ geometry
0 {0B2364ED-49E0-4D53-A33C-C684DD530B57} 20181122133804 20121026122131 2012 10 2020 1 Verbessert AV 2019 ... Kanton 2019_Aufbau CH 18 NaN 710530.0 0 Graubünden 198379.0 POLYGON ((8.87705 46.81291, 8.87783 46.81308, ...
1 {DDD56CEF-0E61-4EED-85ED-F67A459C93ED} 20191106075758 20121026122131 2012 10 2020 1 Verbessert swisstopo 2019 ... Kanton 2019_Aufbau CH 2 11897.0 595951.0 1 Bern 1034977.0 POLYGON ((8.04694 46.78711, 8.05029 46.78834, ...
2 {54B25E50-30A7-4995-ADE3-5FFF6E13A995} 20181122133804 20121026122131 2012 10 2020 1 Verbessert AV 2019 ... Kanton 2019_Aufbau CH 23 1060.0 522463.0 0 Valais 343955.0 POLYGON ((8.38472 46.45216, 8.38364 46.45151, ...
3 {921DFEF2-6D91-4CB8-9CFC-2A831C412020} 20191106075758 20121026122131 2012 10 2020 1 Verbessert swisstopo 2019 ... Kanton 2019_Aufbau CH 22 39097.0 321202.0 1 Vaud 799145.0 POLYGON ((7.07124 46.20101, 7.06621 46.19987, ...
4 {95F10F52-8B2F-4D6A-AF7E-D4F915E42F89} 20181122133804 20121026122131 2012 10 2020 1 Verbessert AV 2019 ... Kanton 2019_Aufbau CH 21 7147.0 281216.0 0 Ticino 353343.0 POLYGON ((8.38472 46.45216, 8.38474 46.45231, ...

5 rows × 21 columns

geodf.info()
# Load PLOTLY library
import plotly.express as px
# List of diverging color scales for choropleth maps
fig = px.colors.diverging.swatches_continuous()
fig.show()

A First Choropleth Map

Use population ("EINWOHNERZ") feature of the dataset

# Create Choropleth GeoMap with Population Data (Feature "EINWOHNERZ")
fig = px.choropleth_mapbox(
    geodf,
    geojson=geodf.geometry,
    locations=geodf.index,
    color='EINWOHNERZ',                                          # define feature variable
    color_continuous_scale=px.colors.diverging.Geyser,           # built in color scale
    labels={'EINWOHNERZ':'Einwohnerzahl 2020'},
    hover_name='NAME',
    hover_data={'KANTONSNUM':True, 'NAME':True, 'EINWOHNERZ':True},
    opacity=0.5,
    center=dict(lat=46.94809, lon=7.44744),                      # capital Bern as map center
    zoom=6.5,
    mapbox_style="carto-positron"                                # options "open-street-map"
)

fig.update_layout(margin=dict(l=0, r=0, t=0, b=0))

fig.show()

A Second Choropleth Map

Use area ("KANTONSFLA") feature of the dataset

# Create Choropleth GeoMap with Area Data (Feature "KANTOSNFLA")
fig = px.choropleth_mapbox(
    geodf,
    geojson=geodf.geometry,
    locations=geodf.index,
    color='KANTONSFLA',                                          # define feature variable
    color_continuous_scale=px.colors.diverging.delta,            # built in color scale
    labels={'KANTONSFLA':'Kantonsfläche 2020'},
    hover_name='NAME',
    hover_data={'KANTONSNUM':True, 'NAME':True, 'KANTONSFLA':True},
    opacity=0.5,
    center=dict(lat=46.94809, lon=7.44744),                      # capital Bern as map center
    zoom=6.5,
    mapbox_style="carto-positron"                                # options "open-street-map"
)

fig.update_layout(margin=dict(l=0, r=0, t=0, b=0))

fig.show()

Example 2: A Swiss Choropleth Plot Map using Swiss Cantons and BFS Data

Load the Swiss Cantons raw GeoData and Pre-Process the correspnding geodf GeoFrame

# Load GEOPANDAS library
import geopandas as gpd
# read Swiss Cantons Data + Geometry
geojson_file = 'https://raw.githubusercontent.com/sawubona-repo/BINA-FS24-WORK/master/zDiversExamples/Notebook-GeoMapping/DATA/ch-cantons.geojson'

# Read GeoJSON file into GeoDataFrame
raw_geodf = gpd.read_file(geojson_file)
raw_geodf.info()
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
WARNING:fiona.ogrext:Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
<class 'geopandas.geodataframe.GeoDataFrame'>
RangeIndex: 51 entries, 0 to 50
Data columns (total 21 columns):
 #   Column      Non-Null Count  Dtype   
---  ------      --------------  -----   
 0   UUID        51 non-null     object  
 1   DATUM_AEND  51 non-null     object  
 2   DATUM_ERST  51 non-null     object  
 3   ERSTELL_J   51 non-null     int64   
 4   ERSTELL_M   51 non-null     object  
 5   REVISION_J  51 non-null     int64   
 6   REVISION_M  51 non-null     object  
 7   GRUND_AEND  51 non-null     object  
 8   HERKUNFT    51 non-null     object  
 9   HERKUNFT_J  51 non-null     int64   
 10  HERKUNFT_M  51 non-null     object  
 11  OBJEKTART   51 non-null     object  
 12  REVISION_Q  51 non-null     object  
 13  ICC         51 non-null     object  
 14  KANTONSNUM  51 non-null     int64   
 15  SEE_FLAECH  19 non-null     float64 
 16  KANTONSFLA  26 non-null     float64 
 17  KT_TEIL     51 non-null     object  
 18  NAME        51 non-null     object  
 19  EINWOHNERZ  26 non-null     float64 
 20  geometry    51 non-null     geometry
dtypes: float64(3), geometry(1), int64(4), object(13)
memory usage: 8.5+ KB
# reduce the GeoFrame to necessary features/columns
geodf = raw_geodf
geodf.drop(["UUID", "DATUM_AEND","DATUM_ERST","ERSTELL_J","ERSTELL_M", "REVISION_J", "REVISION_M", "GRUND_AEND", "HERKUNFT", "HERKUNFT_J", "HERKUNFT_M", "OBJEKTART", "REVISION_Q", "ICC"], axis=1, inplace=True)
geodf.head(3)
KANTONSNUM SEE_FLAECH KANTONSFLA KT_TEIL NAME EINWOHNERZ geometry
0 18 NaN 710530.0 0 Graubünden 198379.0 POLYGON ((8.87705 46.81291, 8.87783 46.81308, ...
1 2 11897.0 595951.0 1 Bern 1034977.0 POLYGON ((8.04694 46.78711, 8.05029 46.78834, ...
2 23 1060.0 522463.0 0 Valais 343955.0 POLYGON ((8.38472 46.45216, 8.38364 46.45151, ...
# Rename and homogenize feature/column names in the Geoframe
geodf.rename(columns = {'KANTONSNUM':'KantonNr', 'NAME':'Kanton', 'SEE_FLAECH':'Fläche_See','KANTONSFLA':'Fläche_Kanton','EINWOHNERZ':'Anzahl_Einwohner_2020'}, inplace = True)

geodf.info()
# Fill NA values and convert feature values to integers
geodf['Anzahl_Einwohner_2020'] = geodf['Anzahl_Einwohner_2020'].fillna(0).astype(int)
geodf['Anzahl_Einwohner_2020'] = geodf['Anzahl_Einwohner_2020'].astype(int)

geodf.info()
<class 'geopandas.geodataframe.GeoDataFrame'>
RangeIndex: 51 entries, 0 to 50
Data columns (total 7 columns):
 #   Column                 Non-Null Count  Dtype   
---  ------                 --------------  -----   
 0   KantonNr               51 non-null     int64   
 1   Fläche_See             19 non-null     float64 
 2   Fläche_Kanton          26 non-null     float64 
 3   KT_TEIL                51 non-null     object  
 4   Kanton                 51 non-null     object  
 5   Anzahl_Einwohner_2020  51 non-null     int64   
 6   geometry               51 non-null     geometry
dtypes: float64(2), geometry(1), int64(2), object(2)
memory usage: 2.9+ KB

Load the Swiss Federal Statistics (BFS) raw Data and Pre-Process the correspnding bfsdf DataFrame

see BFS for the original data source

import pandas as pd
# read BFS Data "Ständige Wohnbevölkerung, Stand 2022"
raw_bfs_file = 'https://raw.githubusercontent.com/sawubona-repo/BINA-FS24-WORK/main/zDiversExamples/DATA/BFS/BFS-CH-Kantone-St%C3%A4ndige-Wohnbev%C3%B6lkerung-2022.csv'

# Read CSV file into DataFrame
raw_bfsdf = pd.read_csv(bfs_file, sep=';')
raw_bfsdf.head()
KantonNr KantonKürzel Kanton Total Alter-0–19 Alter-20–64 Alter-65-und-mehr Mann Frau Schweizer ... Ledig Verheiratet Verwitwet Geschieden Unverheiratet EingetragenePartnerschaft AufgelöstePartnerschaft StädtischerKernraum EinflussgebietstädtischerKerne GebieteausserhalbEinflussgebietstädtischerKerne
0 19 AG Aargau 711232 144909 433224 133099 357753 353479 524909 ... 308288 310713 31059 60103 32 803 212 388542 222279 100411
1 16 AI Appenzell I. Rh. 16416 3392 9674 3350 8408 8008 14510 ... 7420 7045 855 1083 1 9 3 0 0 16416
2 15 AR Appenzell A. Rh. 55759 11365 32747 11647 28114 27645 46395 ... 24016 23935 2744 4983 1 66 14 15744 26922 13093
3 2 BE Bern 1051437 200509 620777 230151 516805 534632 872661 ... 469022 431354 54214 94967 57 1389 416 551861 234361 265215
4 13 BL Basel-Landschaft 294417 56709 170422 67286 144441 149976 223876 ... 121799 130051 15814 26126 13 504 104 199822 87691 6904

5 rows × 21 columns

raw_bfsdf.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 26 entries, 0 to 25
Data columns (total 21 columns):
 #   Column                                           Non-Null Count  Dtype 
---  ------                                           --------------  ----- 
 0   KantonNr                                         26 non-null     int64 
 1   KantonKürzel                                     26 non-null     object
 2   Kanton                                           26 non-null     object
 3   Total                                            26 non-null     int64 
 4   Alter-0–19                                       26 non-null     int64 
 5   Alter-20–64                                      26 non-null     int64 
 6   Alter-65-und-mehr                                26 non-null     int64 
 7   Mann                                             26 non-null     int64 
 8   Frau                                             26 non-null     int64 
 9   Schweizer                                        26 non-null     int64 
 10  Ausländer                                        26 non-null     int64 
 11  Ledig                                            26 non-null     int64 
 12  Verheiratet                                      26 non-null     int64 
 13  Verwitwet                                        26 non-null     int64 
 14  Geschieden                                       26 non-null     int64 
 15  Unverheiratet                                    26 non-null     int64 
 16  EingetragenePartnerschaft                        26 non-null     int64 
 17  AufgelöstePartnerschaft                          26 non-null     int64 
 18  StädtischerKernraum                              26 non-null     int64 
 19  EinflussgebietstädtischerKerne                   26 non-null     int64 
 20  GebieteausserhalbEinflussgebietstädtischerKerne  26 non-null     int64 
dtypes: int64(19), object(2)
memory usage: 4.4+ KB
# Rename and homogenize feature/column names in the Dataframe
bfsdf = raw_bfsdf
bfsdf.rename(columns = {'Total':'Anzahl_Einwohner_2022'}, inplace = True)

bfsdf.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 26 entries, 0 to 25
Data columns (total 21 columns):
 #   Column                                           Non-Null Count  Dtype 
---  ------                                           --------------  ----- 
 0   KantonNr                                         26 non-null     int64 
 1   KantonKürzel                                     26 non-null     object
 2   Kanton                                           26 non-null     object
 3   Anzahl_Einwohner_2022                            26 non-null     int64 
 4   Alter-0–19                                       26 non-null     int64 
 5   Alter-20–64                                      26 non-null     int64 
 6   Alter-65-und-mehr                                26 non-null     int64 
 7   Mann                                             26 non-null     int64 
 8   Frau                                             26 non-null     int64 
 9   Schweizer                                        26 non-null     int64 
 10  Ausländer                                        26 non-null     int64 
 11  Ledig                                            26 non-null     int64 
 12  Verheiratet                                      26 non-null     int64 
 13  Verwitwet                                        26 non-null     int64 
 14  Geschieden                                       26 non-null     int64 
 15  Unverheiratet                                    26 non-null     int64 
 16  EingetragenePartnerschaft                        26 non-null     int64 
 17  AufgelöstePartnerschaft                          26 non-null     int64 
 18  StädtischerKernraum                              26 non-null     int64 
 19  EinflussgebietstädtischerKerne                   26 non-null     int64 
 20  GebieteausserhalbEinflussgebietstädtischerKerne  26 non-null     int64 
dtypes: int64(19), object(2)
memory usage: 4.4+ KB

Feature Engineering with a selection bfsdf_sel of the entire bfsdf DataFrame

# define selection
bfsdf_sel = bfsdf[['KantonNr', 'Kanton', 'Anzahl_Einwohner_2022', 'Mann', 'Frau', 'Geschieden', 'Schweizer', 'StädtischerKernraum']]
bfsdf_sel.head()
KantonNr Kanton Anzahl_Einwohner_2022 Mann Frau Geschieden Schweizer StädtischerKernraum
0 19 Aargau 711232 357753 353479 60103 524909 388542
1 16 Appenzell I. Rh. 16416 8408 8008 1083 14510 0
2 15 Appenzell A. Rh. 55759 28114 27645 4983 46395 15744
3 2 Bern 1051437 516805 534632 94967 872661 551861
4 13 Basel-Landschaft 294417 144441 149976 26126 223876 199822
# Add additional features, proportional values etc.

# Calculate the fraction of "Schweizer" on the "Anzahl_Einwohner_2022"
bfsdf_sel["zAnteil_Schweizer"] = bfsdf_sel.apply(lambda row: row['Schweizer'] / row['Anzahl_Einwohner_2022'], axis=1)

# Calculate the ratio of "Mann" versus "Frau"
bfsdf_sel["zRatio_MannFrau"] = bfsdf_sel.apply(lambda row: row['Mann'] / row['Frau'], axis=1)

# Calculate the fraction of "Schweizer" on the "Anzahl_Einwohner_2022"
bfsdf_sel["zAnteil_Geschieden"] = bfsdf_sel.apply(lambda row: row['Geschieden'] / row['Anzahl_Einwohner_2022'], axis=1)

# Calculate the ratio of "StädtischerKernraum" versus "Anzahl_Einwohner_2022"
bfsdf_sel["zRatio_Städtisch"] = bfsdf_sel.apply(lambda row: row['StädtischerKernraum'] / row['Anzahl_Einwohner_2022'], axis=1)
<ipython-input-71-dfd37ee35fc7>:4: SettingWithCopyWarning:


A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy

<ipython-input-71-dfd37ee35fc7>:7: SettingWithCopyWarning:


A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy

<ipython-input-71-dfd37ee35fc7>:10: SettingWithCopyWarning:


A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy

<ipython-input-71-dfd37ee35fc7>:13: SettingWithCopyWarning:


A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy

bfsdf_sel.head()
KantonNr Kanton Anzahl_Einwohner_2022 Mann Frau Geschieden Schweizer StädtischerKernraum zAnteil_Schweizer zRatio_MannFrau zAnteil_Geschieden zRatio_Städtisch
0 19 Aargau 711232 357753 353479 60103 524909 388542 0.738028 1.012091 0.084505 0.546294
1 16 Appenzell I. Rh. 16416 8408 8008 1083 14510 0 0.883894 1.049950 0.065972 0.000000
2 15 Appenzell A. Rh. 55759 28114 27645 4983 46395 15744 0.832063 1.016965 0.089367 0.282358
3 2 Bern 1051437 516805 534632 94967 872661 551861 0.829970 0.966656 0.090321 0.524864
4 13 Basel-Landschaft 294417 144441 149976 26126 223876 199822 0.760404 0.963094 0.088738 0.678704

Combine GeoFrame and DataFrame for Visual Data Analysis

#merge GeoFrame with DataFrame on corresponding "KantonNr" column values
joined_geodf = pd.merge(geodf, bfsdf_sel, on=["KantonNr"])

joined_geodf.shape
joined_geodf.info()
<class 'geopandas.geodataframe.GeoDataFrame'>
Int64Index: 51 entries, 0 to 50
Data columns (total 18 columns):
 #   Column                 Non-Null Count  Dtype   
---  ------                 --------------  -----   
 0   KantonNr               51 non-null     int64   
 1   Fläche_See             19 non-null     float64 
 2   Fläche_Kanton          26 non-null     float64 
 3   KT_TEIL                51 non-null     object  
 4   Kanton_x               51 non-null     object  
 5   Anzahl_Einwohner_2020  51 non-null     int64   
 6   geometry               51 non-null     geometry
 7   Kanton_y               51 non-null     object  
 8   Anzahl_Einwohner_2022  51 non-null     int64   
 9   Mann                   51 non-null     int64   
 10  Frau                   51 non-null     int64   
 11  Geschieden             51 non-null     int64   
 12  Schweizer              51 non-null     int64   
 13  StädtischerKernraum    51 non-null     int64   
 14  zAnteil_Schweizer      51 non-null     float64 
 15  zRatio_MannFrau        51 non-null     float64 
 16  zAnteil_Geschieden     51 non-null     float64 
 17  zRatio_Städtisch       51 non-null     float64 
dtypes: float64(6), geometry(1), int64(8), object(3)
memory usage: 7.6+ KB
joined_geodf.head()
KantonNr Fläche_See Fläche_Kanton KT_TEIL Kanton_x Anzahl_Einwohner_2020 geometry Kanton_y Anzahl_Einwohner_2022 Mann Frau Geschieden Schweizer StädtischerKernraum zAnteil_Schweizer zRatio_MannFrau zAnteil_Geschieden zRatio_Städtisch
0 18 NaN 710530.0 0 Graubünden 198379 POLYGON ((8.87705 46.81291, 8.87783 46.81308, ... Graubünden 202538 101760 100778 17628 162686 66483 0.803237 1.009744 0.087036 0.328250
1 2 11897.0 595951.0 1 Bern 1034977 POLYGON ((8.04694 46.78711, 8.05029 46.78834, ... Bern 1051437 516805 534632 94967 872661 551861 0.829970 0.966656 0.090321 0.524864
2 2 NaN NaN 2 Bern 0 POLYGON ((7.55835 47.32237, 7.55716 47.32262, ... Bern 1051437 516805 534632 94967 872661 551861 0.829970 0.966656 0.090321 0.524864
3 2 NaN NaN 3 Bern 0 POLYGON ((7.12304 46.90020, 7.12136 46.90051, ... Bern 1051437 516805 534632 94967 872661 551861 0.829970 0.966656 0.090321 0.524864
4 2 NaN NaN 4 Bern 0 POLYGON ((7.09086 46.90382, 7.09089 46.90336, ... Bern 1051437 516805 534632 94967 872661 551861 0.829970 0.966656 0.090321 0.524864
geodf2 = joined_geodf

Create Geomaps for Visual Data Analysis

1) Choropleth Map for Citizenship Ratio in Swiss Cantons

# Load PLOTLY library
import plotly.express as px
# Create Choropleth GeoMap with Population Data (Feature "zAnteil_Schweizer")
fig = px.choropleth_mapbox(
    geodf2,
    geojson=geodf2.geometry,
    locations=geodf2.index,
    color='zAnteil_Schweizer',                                   # define feature variable
    color_continuous_scale=px.colors.diverging.Geyser,           # built in color scale
    labels={'zAnteil_Schweizer':'Anteil Schweizer an der Gesamtbevölkerung 2022'},
    hover_name='KantonNr',
    hover_data={'KantonNr':True, 'Kanton_x':True, 'Anzahl_Einwohner_2022':True, 'zAnteil_Schweizer':True},
    opacity=0.5,
    center=dict(lat=46.94809, lon=7.44744),                      # capital Bern as map center
    zoom=6.5,
    mapbox_style="carto-positron"                                # options "open-street-map"
)

fig.update_layout(margin=dict(l=0, r=0, t=0, b=0))

fig.show()

2) Choropleth Map for Gender Ratio in Swiss Cantons

# Create Choropleth GeoMap with Population Data (Feature "zRatio_MannFrau")
fig = px.choropleth_mapbox(
    geodf2,
    geojson=geodf2.geometry,
    locations=geodf2.index,
    color='zRatio_MannFrau',                                     # define feature variable
    color_continuous_scale=px.colors.diverging.RdBu,             # built in color scale
    labels={'zRatio_MannFrau':'Ratio Mann vs. Frau in der Gesamtbevölkerung 2022'},
    hover_name='KantonNr',
    hover_data={'KantonNr':True, 'Kanton_x':True, 'Anzahl_Einwohner_2022':True, 'zRatio_MannFrau':True},
    opacity=0.5,
    center=dict(lat=46.94809, lon=7.44744),                      # capital Bern as map center
    zoom=6.5,
    mapbox_style="carto-positron"                                # options "open-street-map"
)

fig.update_layout(margin=dict(l=0, r=0, t=0, b=0))

fig.show()

3) Choropleth Map for Urban Population Ratio

# Create Choropleth GeoMap with Population Data (Feature "zRatio_Städtisch")
fig = px.choropleth_mapbox(
    geodf2,
    geojson=geodf2.geometry,
    locations=geodf2.index,
    color='zRatio_Städtisch',                                     # define feature variable
    color_continuous_scale=px.colors.diverging.PuOr,              # built in color scale
    labels={'zRatio_Städtisch':'Ratio städtische Population an der Gesamtbevölkerung 2022'},
    hover_name='KantonNr',
    hover_data={'KantonNr':True, 'Kanton_x':True, 'Anzahl_Einwohner_2022':True, 'zRatio_Städtisch':True},
    opacity=0.5,
    center=dict(lat=46.94809, lon=7.44744),                      # capital Bern as map center
    zoom=6.5,
    mapbox_style="carto-positron"                                # options "open-street-map"
)

fig.update_layout(margin=dict(l=0, r=0, t=0, b=0))

fig.show()